package com.qifan.energy.netWork;


import com.qifan.energy.bean.result.GameExitNotifyResult;
import com.qifan.energy.bean.result.GameSocketLoginResult;
import com.qifan.energy.bean.result.GameStakeResultNotify;
import com.qifan.energy.bean.result.GameUserStakePush;
import com.qifan.energy.bean.result.UserStartPushResult;
import com.qifan.energy.mvp.callback.ICallBack;
import com.qifan.energy.mvp.callback.ISuccessCallBack;
import com.qifan.energy.mvp.callback.ISuccessCallBack2;
import com.qifan.energy.bean.request.GameLoginParam2;
import com.qifan.energy.bean.result.GameStakeBeginPush;
import com.qifan.energy.bean.result.GameSyncPushResult;
import com.qifan.energy.bean.result.RoomListItem;
import com.qifan.energy.bean.result.UserOnlineNotifyResult;
import com.qifan.energy.bean.result.UserReadyPushResult;
import com.qifan.energy.global.Constant;
import com.qifan.energy.global.Keys;
import com.qifan.energy.util.GsonUtils;
import com.qifan.energy.util.MapUtils;
import com.qifan.energy.util.ThreadUtils;
import com.orhanobut.logger.Logger;
import com.qifan.energy.util.ToastUtils;
import com.qifan.energy.util.eventbus.EventBusUtils;
import com.qifan.energy.util.eventbus.EventMessage;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import io.socket.client.Ack;
import io.socket.client.IO;
import io.socket.client.Manager;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
import io.socket.engineio.client.Transport;

public class SocketIOClient {

    private Socket mSocket;
    private boolean isConnected = false;//默认没有连接
    private boolean hasEnterTable = false;//是否已经进入桌
    private boolean hasEnterRoom = false;//是否已经进入房间
    /*private GameSocketLoginResult loginResult = null;//玩家登陆结果

    public GameSocketLoginResult getLoginResult() {
        return loginResult;
    }

    public void setLoginResult(GameSocketLoginResult loginResult) {
        this.loginResult = loginResult;
    }*/

    public boolean isConnected() {
        return isConnected;
    }

    public void setConnected(boolean connected) {
        isConnected = connected;
    }

    public boolean isHasEnterTable() {
        return hasEnterTable;
    }

    public void setHasEnterTable(boolean hasEnterTable) {
        this.hasEnterTable = hasEnterTable;
    }

    public boolean isHasEnterRoom() {
        return hasEnterRoom;
    }

    public void setHasEnterRoom(boolean hasEnterRoom) {
        this.hasEnterRoom = hasEnterRoom;
    }

    public static SocketIOClient ioClient = null;
    public static SocketIOClient getInstance() {
        if (ioClient == null) {
            synchronized (SocketIOClient.class) {
                ioClient = new SocketIOClient();
            }
        }
        return ioClient;
    }
    private SocketIOClient() {
        initSocket();
    }

    /* 初始化Socket实例 */
    public void initSocket() {
        try {
            IO.Options op = new IO.Options();
            op.forceNew = true;
            op.reconnection = false;
            /*op.transports = new String[]{"websocket"};
            op.reconnection = true;*/
            //设置环节，IO.socket（）里的内容是你服务器端的地址以及监听的端口
            String gameBaseUrl = "Constant.BaseUrl" + ":10009";
            mSocket = IO.socket(gameBaseUrl, op);
            setOnEventConnect();
            setOnEventInRoom();
            setOnEventWithPlaying();
            mSocket.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /* 获取Socket实例 */
    public Socket getmSocket() {
        return mSocket;
    }

    /* 基本连接监听 */
    private void setOnEventConnect(){
        // 监听到Socket.EVENT_CONNECT这个定义好的命令,则说明连接客户端和服务器
        getmSocket().on(Socket.EVENT_CONNECT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                setConnected(true);
                Logger.i("连接成功");
            }
        }).on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() { //监听Socket连接是中断。
            @Override
            public void call(Object... args) {
                //JSONObject obj = (JSONObject)args[0];
                Logger.i("连接出错");
            }
        }).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                setConnected(false);
                Logger.i("连接断开");
            }
        }).on(Socket.EVENT_CONNECT_TIMEOUT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Logger.i("连接超时");
            }
        });
    }

    /* socket连接 */
    public void connect(){
        if (!isConnected) {
            getmSocket().connect();
            setConnected(true);
        }
    }

    /* 断开连接 */
    public void disconnect(){
        if (isConnected) {
            getmSocket().disconnect();
            setConnected(false);
        }
    }

    /* 释放监听事件 */
    private void offWithEvent(String event){
        getmSocket().off(event, null);
    }

    //取消连接Server
    private void offAll(){
        //释放基本配置
        offWithEvent(Socket.EVENT_CONNECT);
        offWithEvent(Socket.EVENT_CONNECT_ERROR);
        offWithEvent(Socket.EVENT_DISCONNECT);
        offWithEvent(Socket.EVENT_CONNECT_TIMEOUT);

        /*getmSocket().off(Socket.EVENT_CONNECT_ERROR, null);
        getmSocket().off(Socket.EVENT_CONNECT_TIMEOUT, null);*/
        offWithEvent(Keys.SOCKET_ON.LOGIN_RESULT);
        offWithEvent(Keys.SOCKET_ON.GAME_PONG);
        offWithEvent(Keys.SOCKET_ON.NEW_USER_COMES_PUSH);
        offWithEvent(Keys.SOCKET_ON.ROOM_PERSONS_NOTIFY);
        offWithEvent(Keys.SOCKET_ON.USER_READY_PUSH);
        offWithEvent(Keys.SOCKET_ON.USER_START_PUSH);
        offWithEvent(Keys.SOCKET_ON.USER_STAKE_PUSH);
        offWithEvent(Keys.SOCKET_ON.EXIT_NOTIFY);
        offWithEvent(Keys.SOCKET_ON.EXIT_RESULT);
        offWithEvent(Keys.SOCKET_ON.TIME_OUT);
        offWithEvent(Keys.SOCKET_ON.STAKE_RESULT_NOTIFY);
        offWithEvent(Keys.SOCKET_ON.USER_ONLINE_NOTIFY);
    }

    /* 进入房间事件监听 */
    public void setOnEventInRoom(){
        roomPersonsNotify(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                RoomListItem result = GsonUtils.getGson().fromJson(data.toString(), RoomListItem.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.ROOM_PERSONS_NOTIFY,result));
            }
        });
    }

    /* 进入桌事件监听 */
    public void setOnEventWithPlaying(){
        //登录接收事件
        loginResult(new ICallBack<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameSocketLoginResult result = GsonUtils.getGson().fromJson(data.toString(), GameSocketLoginResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.LOGIN_RESULT,result));
            }

            @Override
            public void onFailed(String error) {
                ToastUtils.showShort("登录失败");
            }
        });
        //玩家进入房间牌桌或上线
        newUserComesPush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameSocketLoginResult.SeatsBean result = GsonUtils.getGson().fromJson(data.toString(), GameSocketLoginResult.SeatsBean.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.NEW_USER_COMES_PUSH,result));
            }
        });
        //玩家在线状态
        userOnlineNotify(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                UserOnlineNotifyResult result = GsonUtils.getGson().fromJson(data.toString(), UserOnlineNotifyResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.USER_ONLINE_NOTIFY,result));
            }
        });
        //游戏准备阶段开始
        readyBeginNotify(new ISuccessCallBack() {
            @Override
            public void onSuccess() {
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.READY_BEGIN_NOTIFY,Keys.SOCKET_ON.READY_BEGIN_NOTIFY));
            }
        });
        //玩家已准备
        userReadyPush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                UserReadyPushResult result = GsonUtils.getGson().fromJson(data.toString(), UserReadyPushResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.USER_READY_PUSH,result));
            }
        });
        //玩家已开始
        userStartPush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                UserStartPushResult result = GsonUtils.getGson().fromJson(data.toString(), UserStartPushResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.USER_START_PUSH,result));
            }
        });
        //游戏下注阶段开始
        stakeBeginPush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameStakeBeginPush result = GsonUtils.getGson().fromJson(data.toString(), GameStakeBeginPush.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.STAKE_BEGIN_PUSH,result));
            }
        });
        //玩家已押注
        userStakePush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameUserStakePush result = GsonUtils.getGson().fromJson(data.toString(), GameUserStakePush.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.USER_STAKE_PUSH,result));
            }
        });
        //下注结果
        stakeResultNotify(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameStakeResultNotify result = GsonUtils.getGson().fromJson(data.toString(), GameStakeResultNotify.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.STAKE_RESULT_NOTIFY,result));
            }
        });
        //玩家掉线再次上线同步游戏数据
        gameSyncPush(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameSyncPushResult result = GsonUtils.getGson().fromJson(data.toString(), GameSyncPushResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.GAME_SYNC_PUSH,result));
            }
        });
        //玩家退出房间通知(other所有人收到)
        exitNotify(new ISuccessCallBack2<JSONObject>() {
            @Override
            public void onSuccess(JSONObject data) {
                GameExitNotifyResult result = GsonUtils.getGson().fromJson(data.toString(), GameExitNotifyResult.class);
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.EXIT_NOTIFY,result));
            }
        });
        //玩家退出结果(自己收到)
        exitResult(new ICallBack() {
            @Override
            public void onSuccess(Object data) {
                //断开连接
                disconnect();
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.EXIT_RESULT,Keys.SOCKET_ON.EXIT_RESULT));
            }

            @Override
            public void onFailed(String error) {
                ToastUtils.showShort(error);
            }
        });
        //玩家时间卡用尽
        timeOut(new ISuccessCallBack() {
            @Override
            public void onSuccess() {
                //断开连接
                disconnect();
                EventBusUtils.post(new EventMessage(Keys.SOCKET_ON.TIME_OUT,Keys.SOCKET_ON.TIME_OUT));
            }
        });
    }

    //----------------------emit 发送--------------------//
    /* 用户登录 */
    public void login(GameLoginParam2 param2){
        connect();
        JSONObject jsonObject = MapUtils.jsonObjectWithObject(param2);
        getmSocket().emit(Keys.SOCKET_EMIT.LOGIN,jsonObject);
    }

    /* 玩家准备 */
    public void ready(){
        getmSocket().emit(Keys.SOCKET_EMIT.READY);
    }

    /* 开始游戏 */
    public void start(){
        getmSocket().emit(Keys.SOCKET_EMIT.START);
    }

    /* 玩家下注 //0：石头，1：剪刀，2：布，-1：不出 */
    public void stake(Integer index){
        getmSocket().emit(Keys.SOCKET_EMIT.STAKE,index);
    }

    /* 玩家退出房间 */
    public void exit(){
        getmSocket().emit(Keys.SOCKET_EMIT.EXIT);
    }

    /* 客户端心跳包 */
    public void game_ping(){
        getmSocket().emit(Keys.SOCKET_EMIT.GAME_PING);
    }

    //----------------------on 接收--------------------//
    /* 登录异常结果提示 */
    public void loginResult(ICallBack callBack){
        onListener(Keys.SOCKET_ON.LOGIN_RESULT,callBack);
    }

    /* 客户端心跳答复 */
    public void gamePong(ISuccessCallBack callBack){
        onListener(Keys.SOCKET_ON.GAME_PONG,callBack);
    }

    /* 玩家进入房间牌桌或上线 */
    public void newUserComesPush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.NEW_USER_COMES_PUSH,callBack);
    }

    /* 房间人数通知(大厅广播) */
    public void roomPersonsNotify(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.ROOM_PERSONS_NOTIFY,callBack);
    }

    /* 玩家已准备 */
    public void userReadyPush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.USER_READY_PUSH,callBack);
    }

    /* 玩家已开始 */
    public void userStartPush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.USER_START_PUSH,callBack);
    }

    /* 玩家已押注 */
    public void userStakePush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.USER_STAKE_PUSH,callBack);
    }

    /* 玩家退出房间通知 */
    public void exitNotify(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.EXIT_NOTIFY,callBack);
    }

    /* 玩家退出结果(自己收到) */
    public void exitResult(ICallBack callBack){
        onListener(Keys.SOCKET_ON.EXIT_RESULT,callBack);
    }

    /* 玩家时间卡用尽 */
    public void timeOut(ISuccessCallBack callBack){
        onListener(Keys.SOCKET_ON.TIME_OUT,callBack);
    }

    /* 下注结果 */
    public void stakeResultNotify(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.STAKE_RESULT_NOTIFY, callBack);
    }

    /* 玩家在线状态 */
    public void userOnlineNotify(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.USER_ONLINE_NOTIFY, callBack);
    }

    /* 游戏准备阶段开始 */
    public void readyBeginNotify(ISuccessCallBack callBack){
        onListener(Keys.SOCKET_ON.READY_BEGIN_NOTIFY, callBack);
    }

    /* 游戏下注阶段开始 */
    public void stakeBeginPush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.STAKE_BEGIN_PUSH, callBack);
    }

    /* 玩家掉线再次上线同步游戏数据 */
    public void gameSyncPush(ISuccessCallBack2 callBack){
        onListener(Keys.SOCKET_ON.GAME_SYNC_PUSH, callBack);
    }

    /* 封装一层网络处理方法（有data） */
    public void onListener(String key,ICallBack callBack){
        getmSocket().on(key, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                ThreadUtils.runInUIThread(new Runnable() {
                    @Override
                    public void run() {
                        if (args.length>0) {
                            JSONObject obj = (JSONObject)args[0];
                            Logger.i("SocketIOClient:ICallBack:" + obj.toString());
                            try {
                                int code = obj.getInt("errcode");
                                if (code == 0){
                                    JSONObject data = obj.getJSONObject("data");
                                    if (callBack != null) callBack.onSuccess(data);
                                } else {
                                    String message = obj.getString("errmsg");
                                    if (callBack != null) callBack.onFailed(message);
                                }
                            } catch (JSONException e) {
                                e.printStackTrace();
                                if (callBack != null) callBack.onFailed("解析异常");
                            }
                        } else {
                            if (callBack != null) callBack.onFailed("空数据");
                        }
                    }
                });
            }
        });
    }

    /* 封装一层网络处理方法（无errcode） */
    public void onListener(String key, ISuccessCallBack2 callBack){
        getmSocket().on(key, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                ThreadUtils.runInUIThread(new Runnable() {
                    @Override
                    public void run() {
                        if (args.length>0) {
                            Object object = args[0];
                            if (object instanceof JSONObject){
                                JSONObject obj = (JSONObject)args[0];
                                Logger.i("SocketIOClient:ISuccessCallBack2:" + obj.toString());
                                if (callBack != null) callBack.onSuccess(obj);
                            } else if (object instanceof Integer){
                                JSONObject obj = new JSONObject();
                                try {
                                    obj.put("userid",object);
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                                if (callBack != null) callBack.onSuccess(obj);
                            } else {
                                ToastUtils.showShort("类型转换失败");
                            }
                        }
                    }
                });
            }
        });
    }

    /* 封装一层网络处理方法（无data） */
    public void onListener(String key, ISuccessCallBack callBack){
        getmSocket().on(key, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                ThreadUtils.runInUIThread(new Runnable() {
                    @Override
                    public void run() {
                        Logger.i("SocketIOClient:ISuccessCallBack:" + args.toString());
                        if (callBack != null) callBack.onSuccess();
                    }
                });
            }
        });
    }

    /* 设置Http */
    private void okhttp(){
        /*OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .hostnameVerifier(myHostnameVerifier)
                .sslSocketFactory(mySSLContext.getSocketFactory(), myX509TrustManager)
                .build();

        // default settings for all sockets
        IO.setDefaultOkHttpWebSocketFactory(okHttpClient);
        IO.setDefaultOkHttpCallFactory(okHttpClient);

        // set as an option
        opts = new IO.Options();
        opts.callFactory = okHttpClient;
        opts.webSocketFactory = okHttpClient;
        socket = IO.socket("https://localhost", opts);*/
    }

    /*@Override
    public void onDestroy() {
        super.onDestroy();

        mSocket.disconnect();
        mSocket.off("new message", onNewMessage);
    }*/

    //事件监听 Receiving an object  示例
    public void onListener(){
        getmSocket().on("foo", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                JSONObject obj = (JSONObject)args[0];
            }
        });
    }

    /* 发送消息 */
    public void sendMessage(String message) {
        if (isConnected) {
            getmSocket().emit("foo", "hi");
        }
    }

    // Sending an object
    public void sendJsonObject(JSONObject jsonObject) {
        if (isConnected) {
            getmSocket().emit("APP", jsonObject);
        }
    }

    //测试使用
    public void sendL01(){
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("protocol","L01");
            jsonObject.put("protocol2","L02");
            sendJsonObject(jsonObject);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /* 得到服务端一个心跳 */
    public void sendMessageWithAck(final String event, final Ack ack){
        if (isConnected) {
            getmSocket().emit(event,"",ack);
        }
    }

    // 发送字符串 JSON 等3种格式
    public void sendMessage(JSONObject jsonObject) {
        getmSocket().emit("action");   //表示发送了一个action命令，命令是字符串的，在另一端接收时，可以这么写：
        getmSocket().on("action", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Logger.i("args" + args);
            }
        });

        getmSocket().emit("action","data");    //表示发送了一个action命令，还有data数据，在另一端接收时，可以这么写：
        getmSocket().on("action", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Logger.i("args" + args);
            }
        });

        getmSocket().emit("action","arg1","arg2");   //表示发送了一个action命令，还有两个数据，在另一端接收时，可以这么写：
        getmSocket().on("action", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Logger.i("args" + args);
            }
        });

        getmSocket().emit("foo", "woot", new Ack() {
            @Override
            public void call(Object... args) {
                Logger.i("args" + args);
            }
        });

        getmSocket().on("foo", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Ack ack = (Ack) args[args.length - 1];
                ack.call();
            }
        });
    }

    /* 设置请求头 */
    private void headers(){
        getmSocket().io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                Transport transport = (Transport)args[0];

                transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() {
                    @Override
                    public void call(Object... args) {
                        @SuppressWarnings("unchecked")
                        Map<String, List<String>> headers = (Map<String, List<String>>)args[0];
                        // modify request headers
                        headers.put("Cookie", Arrays.asList("foo=1;"));
                    }
                });

                transport.on(Transport.EVENT_RESPONSE_HEADERS, new Emitter.Listener() {
                    @Override
                    public void call(Object... args) {
                        @SuppressWarnings("unchecked")
                        Map<String, List<String>> headers = (Map<String, List<String>>)args[0];
                        // access response headers
                        String cookie = headers.get("Set-Cookie").get(0);
                    }
                });
            }
        });
    }
}
